Room 提供了一個更高層次的抽象,讓我們可以更安全、高效地使用 SQLite 資料庫。它簡化了資料庫操作,提高了開發效率,並增強了應用程式的穩定性。
三個 Room 元件可讓這些工作流程順暢運作。
//Room
implementation("androidx.room:room-runtime:${rootProject.extra["room_version"]}")
ksp("androidx.room:room-compiler:${rootProject.extra["room_version"]}")
implementation("androidx.room:room-ktx:${rootProject.extra["room_version"]}")
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "items")
data class Item(
@PrimaryKey(autoGenerate = true)
val id: Int,
val name: String,
val price: Double,
val quantity: Int
)
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow
@Dao
interface ItemDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(item: Item)
@Update
suspend fun update(item: Item)
@Delete
suspend fun delete(item: Item)
@Query("SELECT * from items WHERE id = :id")
fun getItem(id: Int): Flow<Item>
@Query("SELECT * from items ORDER BY name ASC")
fun getAllItems(): Flow<List<Item>>
}
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
/**
* Database class with a singleton Instance object.
*/
@Database(entities = [Item::class], version = 1, exportSchema = false)
abstract class InventoryDatabase : RoomDatabase() {
abstract fun itemDao(): ItemDao
companion object {
@Volatile
private var Instance: InventoryDatabase? = null
fun getDatabase(context: Context): InventoryDatabase {
// if the Instance is not null, return it, otherwise create a new database instance.
return Instance ?: synchronized(this) {
Room.databaseBuilder(context, InventoryDatabase::class.java, "item_database")
.build()
.also { Instance = it }
}
}
}
}
import kotlinx.coroutines.flow.Flow
class OfflineItemsRepository(private val itemDao: ItemDao) : ItemsRepository {
override fun getAllItemsStream(): Flow<List<Item>> = itemDao.getAllItems()
override fun getItemStream(id: Int): Flow<Item?> = itemDao.getItem(id)
override suspend fun insertItem(item: Item) = itemDao.insert(item)
override suspend fun deleteItem(item: Item) = itemDao.delete(item)
override suspend fun updateItem(item: Item) = itemDao.update(item)
}
StateFlow 和 SharedFlow
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
val homeUiState: StateFlow<HomeUiState> =
itemsRepository.getAllItemsStream().map { HomeUiState(it) }
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
initialValue = HomeUiState()
)
fun HomeScreen(
navigateToItemEntry: () -> Unit,
navigateToItemUpdate: (Int) -> Unit,
modifier: Modifier = Modifier,
viewModel: HomeViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val homeUiState by viewModel.homeUiState.collectAsState()
Scaffold(
....
HomeBody(
itemList = homeUiState.itemList,
onItemClick = navigateToItemUpdate,
modifier = modifier.fillMaxSize(),
contentPadding = innerPadding,
)
....
@Composable
private fun HomeBody(
itemList: List<Item>,
onItemClick: (Int) -> Unit,
modifier: Modifier = Modifier,
contentPadding: PaddingValues = PaddingValues(0.dp),
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
if (itemList.isEmpty()) {
Text(
text = stringResource(R.string.no_item_description),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(contentPadding),
)
} else {
InventoryList(
itemList = itemList,
onItemClick = { onItemClick(it.id) },
contentPadding = contentPadding,
modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.padding_small))
)
}
}
}
空白db
新增db資料
顯示db資料
更新db資料
刪除db資料
https://developer.android.com/courses/pathways/android-basics-compose-unit-6-pathway-2?hl=zh-tw